package io.github.veigara.tcp.framework.service.impl;


import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.veigara.tcp.framework.model.DeviceConnection;
import io.github.veigara.tcp.framework.model.DeviceConnectionInfo;
import io.github.veigara.tcp.framework.service.DeviceConnectionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class RedisDeviceConnectionServiceImpl implements DeviceConnectionService {
    // 本地缓存设备路由信息，减少Redis访问
    private final ConcurrentHashMap<String, ConcurrentHashMap<String,String>> deviceServerCache = new ConcurrentHashMap<>();

    private static final String DEVICE_CONNECTION_KEY = "device:connection:";
    private static final long CONNECTION_TIMEOUT = 30; // 30分钟

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    private String getDeviceConnectionKey(String deviceTag,String deviceId){
        return DEVICE_CONNECTION_KEY + deviceTag+":"+ deviceId;
    }

    @Override
    public void saveDeviceConnection(String deviceTag,DeviceConnectionInfo connection) {
        try {
            String key = getDeviceConnectionKey(deviceTag,connection.getDeviceId());
            String value = objectMapper.writeValueAsString(connection);
            redisTemplate.opsForValue().set(key, value, CONNECTION_TIMEOUT, TimeUnit.MINUTES);
            deviceServerCache.computeIfAbsent(deviceTag, k -> new ConcurrentHashMap<>()).put(connection.getDeviceId(), connection.getServerAddress());
        } catch (Exception e) {
            throw new RuntimeException("Failed to save device connection", e);
        }
    }

    @Override
    public DeviceConnectionInfo getDeviceConnection(String deviceTag,String deviceId) {
        try {
            String key = getDeviceConnectionKey(deviceTag,deviceId);
            String value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                return objectMapper.readValue(value, DeviceConnectionInfo.class);
            }
            return null;
        } catch (Exception e) {
            throw new RuntimeException("Failed to get device connection", e);
        }
    }

    @Override
    public void removeDeviceConnection(String deviceTag,String deviceId) {
        String key = getDeviceConnectionKey(deviceTag,deviceId);
        redisTemplate.delete(key);
    }

    @Override
    public void updateLastActiveTime(String deviceTag,String deviceId) {
        try {
            String key = getDeviceConnectionKey(deviceTag,deviceId);
            String value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                DeviceConnectionInfo connection = objectMapper.readValue(value, DeviceConnectionInfo.class);
                connection.setLastActiveTime(new Date());
                redisTemplate.opsForValue().set(key, objectMapper.writeValueAsString(connection), CONNECTION_TIMEOUT, TimeUnit.MINUTES);
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to update last active time", e);
        }
    }

    @Override
    public String getDeviceServer(String deviceTag,String deviceId) {
        String serverAddress = null;
        // 先从缓存获取
        ConcurrentHashMap<String, String> deviceTagMap = deviceServerCache.get(deviceTag);
        if(deviceTagMap != null){
            return deviceTagMap.get(deviceId);
        }

        // 从Redis获取
        DeviceConnectionInfo deviceConnection = this.getDeviceConnection(deviceTag,deviceId);
        if (deviceConnection != null) {
            // 更新缓存
            serverAddress = deviceConnection.getServerAddress();
            deviceServerCache.computeIfAbsent(deviceTag, k -> new ConcurrentHashMap<>()).put(deviceId, serverAddress);;
        }
        return serverAddress;
    }

    @Override
    public Map<String, List<DeviceConnection>> getAllDeviceConnections() {
        Map<String, List<DeviceConnection>> res= new HashMap<>();
        try {
            Set<String> keys = redisTemplate.keys(DEVICE_CONNECTION_KEY + "*");
            if (keys != null && !keys.isEmpty()) {
                keys.stream().map(key -> key.substring(DEVICE_CONNECTION_KEY.length())).forEach(deviceTag -> {
                    Set<String> deviceTagKeys = redisTemplate.keys(DEVICE_CONNECTION_KEY + deviceTag + "*");
                    List<String> values = redisTemplate.opsForValue().multiGet(deviceTagKeys);
                    List<DeviceConnection> deviceConnections = values.stream()
                            .map(value -> {
                                try {
                                    return objectMapper.readValue(value, DeviceConnection.class);
                                } catch (Exception e) {
                                    throw new RuntimeException("Failed to parse device connection", e);
                                }
                            }).collect(Collectors.toList());

                    res.put(deviceTag, deviceConnections);
                });
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed to get all device connections", e);
       }
        return res;
    }

    @Override
    public List<DeviceConnection> getAllDeviceConnections(String deviceType) {
        return getAllDeviceConnections().get(deviceType);
    }
}    